package com.shuql.cloud.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;

/**
 * 认证服务器配置
 *
 * @author ：shuquanliang
 * @date ：Created in 2020/4/23 9:13
 * @description：
 */
@Configuration
@EnableAuthorizationServer //开启OAuth2认证服务器功能
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService customUserDetailsService;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private DataSource dataSource;

    /**
     * JWT转换器
     */
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    /**
     * 授权码管理策略
     *
     * @return
     */
    @Bean
    public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
        //JDBC方式保存授权码到oauth_code表中
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * JDBC方式管理客户端信息
     *
     * @return
     */
    @Bean
    public ClientDetailsService jdbcClientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * 配置被允许访问此认证服务器的客户端详情信息
     * 1、内存方式管理
     * 2、数据库方式管理
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 使用内存方式
//        clients.inMemory()
//                .withClient("shuql-pc") // 客户端id
//                .secret(passwordEncoder.encode("shuql-secret")) // 客户端密码，要加密,不然一直要求登录, 获取不到令牌, 而且一定不能被泄露
//                .resourceIds("product-server") // 资源id, 如商品资源
//                .authorizedGrantTypes("authorization_code", "password", "refresh_token", "implicit")// 授权类型, 可同时支持多种授权类型
//                .scopes("all") // 授权范围标识，哪部分资源可访问（all是标识，不是代表所有）
//                .autoApprove(false) // false 跳转到授权页面手动点击授权，true 不用手动授权，直接响应授权码
//                .redirectUris("https://www.baidu.com/"); // 客户端回调地址
        //使用 JDBC 管理客户端信息
        clients.withClientDetails(jdbcClientDetailsService());

    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //密码模式要设置认证管理器
        endpoints.authenticationManager(authenticationManager);
        //刷新令牌获取新令牌时需要
        endpoints.userDetailsService(customUserDetailsService);
        //令牌管理策略
        endpoints.tokenStore(tokenStore)
                //JWT转换器
                .accessTokenConverter(jwtAccessTokenConverter);
        //授权码管理策略，针对授权码模式有效，会将授权码放到 auth_code 表，授权后就会删除它
        endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices());
    }

    /**
     * 令牌端点的安全配置
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //所有人可访问 /oauth/token_key 后面要获取公钥, 默认拒绝访问
        security.tokenKeyAccess("permitAll()");
        //认证后可访问 /oauth/check_token , 默认拒绝访问
        security.checkTokenAccess("isAuthenticated()");
    }
}
